import hudson.model.*;
import com.fasterxml.jackson.databind.ObjectMapper

pipeline {
    agent any
    // 环境变量
    environment {
        // docker私有仓库凭证
        harbor_docker_credentials = credentials('harbor-docker')

        successInfo = null
        failInfo = null
    }
    // 入参定义
    parameters {
        string(name: 'project_name', defaultValue: 'phip', description: '项目名称')
        string(name: 'git_url', defaultValue: 'http://admin@172.16.108.28:8901/r/lzs-phip.git', description: '仓库地址')
        string(name: 'branch_name', defaultValue: 'dockerSwarm', description: 'git分支')
        string(name: 'profiles', defaultValue: 'prod', description: '环境')
        string(name: 'registry_url', defaultValue: '193.193.188.91', description: '镜像仓库地址')
        string(name: 'registry_ns', defaultValue: 'lzs-phip', description: '镜像命名空间')
        string(name: 'pull_image_url', defaultValue: '10.15.3.32:1801', description: '拉取镜像地址')
        // string(name: 'user_email', defaultValue: '125139887@qq.com,1002427501@qq.com,239727268@qq.com,1642194052@qq.com,1041358590@qq.com,1152725570@qq.com,602717724@qq.com,sodream@126.com,354852189@qq.com', description: '邮件通知')
        string(name: 'user_email', defaultValue: '125139887@qq.com,354852189@qq.com', description: '邮件通知')
        string(name: 'service_name', defaultValue: 'service-phip_app-phip', description: '服务名')
    }
    stages {
        stage('检出代码') {
            steps {
                // 检出代码
                checkout([$class                           : 'GitSCM', branches: [[name: "*/${params.branch_name}"]],
                          doGenerateSubmoduleConfigurations: false,
                          extensions                       : [],
                          submoduleCfg                     : [],
                          userRemoteConfigs                : [[
                                                                      credentialsId: 'gitblit',
                                                                      url          : "${params.git_url}"]]])
            }
        }
        stage('打包/构建镜像及推送到docker仓库/执行远程命令') {
            steps {
                script {
                    //========================执行远程命令=======================
                    println('执行远程命令')

                    // 错误列表
                    List successList = new ArrayList();
                    // 错误列表
                    List failList = new ArrayList();
                    //获取发布机构列表
                    json_file = "${env.WORKSPACE}/${project_name}/IpAddrInfo-${params.profiles}.json"
                    def file_contents = readFile json_file
                    def slurped = new groovy.json.JsonSlurperClassic().parseText(file_contents)
                    // root 打包
                    // sh "mvn -B -DskipTests clean package"
                    for (info in slurped) {
                        // 镜像版本
                        def image_tag = sh(returnStdout: true, script: 'echo `date +"%Y%m%d%H%M%S"`').trim()
                        def orgMsg = "=======" + info.orgName + "======" + info.ipAddr + "======"
                        println("执行开始" + orgMsg)
                        //========================编译打包========================
                        println('编译打包')
                        // 切换目录 打包extjs
                        sh "pwd"
                        sh """
                           cd ${project_name}/src/main/resources/debug
                           sudo /root/bin/Sencha/Cmd/6.2.2.36/sencha app build
                           """
                        sh "cd ${env.WORKSPACE}"
                        //sh "echo ${env.WORKSPACE}"
                        //  删除work目录
                        sh "rm -f ${project_name}/app.jar"
                        sh "mvn -B -DskipTests clean package"
                        // 将生成的jar包复制到当前工作空间下
                        sh "cp ${project_name}/target/ROOT.jar ${project_name}/app.jar"
                        sh "ls"
                        //========================构建镜像及推送到docker仓库=======================
                        println('构建镜像及推送到docker仓库')
                        sh "pwd"
                        // 服务器先执行 sudo chmod a+rw /var/run/docker.sock
                        // 登录镜像仓库
                        sh "docker login -u ${harbor_docker_credentials_USR} -p ${harbor_docker_credentials_PSW} ${params.registry_url}"

                        def imageSuffix = "${params.registry_ns}/${params.project_name}:${image_tag}"
                        def localImage = "${params.registry_url}/${imageSuffix}"
                        // 删除 Dockerfile 根据Dockerfile.tpl重新生成
                        sh "rm -f ${project_name}/Dockerfile"
                        sh "sed -e 's#{{env}}#${params.profiles}#g' ${project_name}/Dockerfile.tpl > ${project_name}/Dockerfile"
                        // 构建镜像
                        sh "docker build -t ${localImage} ${project_name}/"
                        // 推送镜像到私服
                        sh "docker push ${localImage}"
                        // 删除当前目录下的app.jar
                        sh "rm -rf ${project_name}/app.jar"
                        //delete docker images
                        sh "docker rmi ${localImage}"

                        def image = "${params.pull_image_url}/${imageSuffix}"

                        // 失败将重试3次
                        for (int i = 0; i < 8; i++) {
                            try {
                                def sshServer = getServer(info.ipAddr.toString(), info.sshPort, info.orgCode)
                                // 测试远程是否可连接
                                sshCommand remote: sshServer, command: "date"
                                sh "echo " + image
                                // 更新或下载镜像
                                sshCommand remote: sshServer, command: "docker service update --image=${image} ${params.service_name}"
                                def successMsg = "发布成功，重试次数 == " + i.toString()
                                successList.add(new IpAddrInfo(orgCode: info.orgCode, orgName: info.orgName, ipAddr: info.ipAddr, msg: successMsg, image: image))
                                sh "echo 执行成功${orgMsg}"
                                break;
                            } catch (ex) {
                                println(ex)
                                if (i != 7) {
                                    sleep(3)
                                    continue;
                                }
                                //记录错误信息
                                failList.add(new IpAddrInfo(orgCode: info.orgCode, orgName: info.orgName, ipAddr: info.ipAddr, msg: ex.message, image: image))
                            }
                        }
                    }
                    //记录成功和失败信息到环境变量
                    ObjectMapper successObj = new ObjectMapper()
                    successInfo = successObj.writeValueAsString(successList)
                    ObjectMapper failObj = new ObjectMapper()
                    failInfo = failObj.writeValueAsString(failList)
                }
            }
        }
        stage('执行成功打印') {
            steps {
                script {
                    println(successInfo)
                }
            }
        }
        stage('执行失败打印') {
            steps {
                script {
                    println(failInfo)
                }
            }
        }
    }
    post('发送通知') {
        success {
            emailext(
                    subject: "【SUCCESS】## 项目构建通知 ## $env.JOB_NAME - Build # $env.BUILD_NUMBER",
                    body: """
~
变动日志
========================
${getChangeString()}
========================

发布成功列表
========================
${successInfo}
========================

发布失败列表
========================
${failInfo}
========================
                    """,
                    to: "${params.user_email}"
            )
        }
        failure {
            emailext(
                    subject: "【FAIL】## 项目构建通知 ##$env.JOB_NAME - Build # $env.BUILD_NUMBER",
                    body: """
~
变动日志
========================
${getChangeString()}
========================

发布成功列表
========================
${successInfo}
========================

发布失败列表
========================
${failInfo}
========================
                    """,
                    to: "${params.user_email}"
            )
        }
    }
}

class IpAddrInfo {
    String orgCode
    String orgName
    String ipAddr
    String msg
    String image
}

// 获取服务器授权
def getServer(ip, port, orgCode) {
    def remote = [:]
    remote.name = "server-${ip}"
    remote.host = ip
    remote.port = port
    remote.allowAnyHosts = true
    withCredentials([usernamePassword(credentialsId: orgCode, passwordVariable: 'password', usernameVariable: 'userName')]) {
        remote.user = "${userName}"
        remote.password = "${password}"
    }
    return remote
}

@NonCPS
def getChangeString() {
    MAX_MSG_LEN = 100
    def changeString = ""
    def changeLogSets = currentBuild.changeSets
    for (int i = 0; i < changeLogSets.size(); i++) {
        def entries = changeLogSets[i].items
        for (int j = 0; j < entries.length; j++) {
            def entry = entries[j]
            truncated_msg = entry.msg.take(MAX_MSG_LEN)
            changeString += "--${truncated_msg}  [${entry.author}]\n"
        }
    }
    if (!changeString) {
        changeString = " - 无"
    }
    echo changeString
    return changeString
}
